

#include "tools/socket.h"
#include "tools/dump.h"
#include "tools/file_util.h"
#include "tools/string_util.h"
#include <string>
#include <fstream>
#include <sstream>
#include <map>
#include <list>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "auth.h"


#define PRINTF(fmt, ...)    (void)(0)

tools::CSocket  sockA(tools::CSocket::eSocketType_TCP);

typedef struct {
    bool        parsed;
    std::string Method;
    std::string Path;
    std::string Proto;
    std::map<std::string, std::string>  mp;
}HttpHead;

class CipherResult {
public:
    CipherResult(std::string& p, std::string& c, std::string& e, std::string& t, std::string& ps)
        : plain(p.c_str()), cipher(c.c_str()), expect(e.c_str()), type(t.c_str()), pass(ps.c_str()) {}
    CipherResult(const CipherResult& o)
        : plain(o.plain.c_str()), cipher(o.cipher.c_str()), expect(o.expect.c_str()), type(o.type.c_str()), pass(o.pass.c_str()) {}
    CipherResult() {}
    std::string plain;
    std::string cipher;
    std::string expect;
    std::string type;
    std::string pass;
};

class Http {
public:
    Http() : buffer(NULL), buflen(0) {
        codemap[200] = "OK";
        codemap[302] = "Found"; 
        codemap[404] = "Not Found";
    }
    ~Http() {}
    void Read(const char * buffer, int len);
    int ParseHead(char * buffer, HttpHead& head);
    int ParseContent(HttpHead& head, const char * buffer, int len);
    std::string MakeHttpHead(int code, int ContextLength);
    std::string ParseFile(const char * file, std::map<std::string, std::string>&  para);
    void MakeRandomPair(std::map<std::string, std::string>& para, std::string& plain, std::string& expect, std::string& type);
    bool IsRandomPairEqual(std::string& t1, std::string& t2, std::string& pass, std::string& type);
    void OutputResult(std::map<std::string, std::string>& para);

    char *  buffer;
    int     buflen;
    std::map<int, const char *>  codemap;
};

void Http::Read(const char * buf, int len)
{
    buffer = (char *)realloc(buffer, buflen + len + 1);
    if (!buffer) {
        throw ("realloc failed.");
    }
    memcpy(buffer + buflen, buf, len);
    buffer[buflen + len] = '\0';
    buflen += len;

    HttpHead    hd;
    hd.parsed = false;
    while (true) {
        if (!hd.parsed) {
            char * p = strstr(buffer, "GET");
            if (!p) {
                p = strstr(buffer, "POST");
            }
            if (!p)
                break;
            char * el = strstr(buffer, "\r\n\r\n");
            if (!el)
                break;
            el+=2;
            char bak = *el;
            *el = '\0';
            int contentlen = ParseHead(p, hd);
            *el = bak;
            len = buflen - (el - buffer);
            if (len <= 0) {
                free(buffer);
                buflen = 0;
                break;
            }
            if (contentlen > 0) {
                if (len >= contentlen) {
                    ParseContent(hd, el, contentlen);
                    el += contentlen;
                    len -= contentlen;
                }
            } else {
                ParseContent(hd, NULL, 0);
            }
            p = (char *)malloc(len + 1);
            memcpy(p, el, len);
            p[len] = '\0';
            free(buffer);
            buffer = p;
            buflen = len;
        } else {
            int cl = atoi(hd.mp["Content-Length"].c_str());
            if (cl > 0) {
                if (buflen > cl) {
                    ParseContent(hd, buffer, cl);
                    int len = buflen - cl;
                    if (len > 0) {
                        char * p = (char*)malloc(len + 1);
                        memcpy(p, buffer + cl, len);
                        free(buffer);
                        buffer = p;
                        buflen = len;
                    } else {
                        free(buffer);
                        buffer = NULL;
                        buflen = 0;
                    }
                }
            } else {
                hd.parsed = false;
            }
        }
    }
}

int Http::ParseHead(char * buffer, HttpHead& head)
{
    PRINTF("parse head: \n%s\n", buffer);
    head.mp.clear();
    head.parsed = true;
    char * p = NULL;
    while ((p = strstr(buffer, "\r\n")) != NULL) {
        *p = '\0';
        char * p1 = strchr(buffer, ':');
        if (p1 != NULL) {
            *p1 = '\0';
            p1++;
            while (*p1 == ' ')
                p1++;
            PRINTF("[%s] => [%s]\n", buffer, p1);
            head.mp[buffer] = p1;
        } else {
            char * t = NULL;
            if (strncmp(buffer, "GET ", 4) == 0) {
                head.Method = "GET";
                t = buffer + 4;
            } else if (strncmp(buffer, "POST ", 5) == 0) {
                head.Method = "POST";
                t = buffer + 5;
            } else {
                PRINTF("unrecognize: [%s]\n", buffer);
            }
            if (t != NULL) {
                while (*t == ' ')
                    t++;
                char * sp = strchr(t, ' '); 
                *sp = '\0';
                head.Path = t;
                sp++;
                while (*sp == ' ')
                    sp++;
                head.Proto = sp;
                PRINTF("[Method] => [%s]\n", head.Method.c_str());
                PRINTF("[Path] => [%s]\n", head.Path.c_str());
                PRINTF("[Proto] => [%s]\n", head.Proto.c_str());
            }
        }
        buffer = p + 2;
    }

    if (head.mp.find("Context-Length") != head.mp.end()) {
        return atoi(head.mp["Context-Length"].c_str());
    }
    return 0;
}


int Http::ParseContent(HttpHead& head, const char * buffer, int len)
{
    head.parsed = false;
    std::string path(".");
    path += head.Path;
    std::map<std::string, std::string>  para;

    size_t pos = path.find_first_of("?");
    if (pos != std::string::npos) {
        std::string temp = path.substr(pos + 1);
        path = path.substr(0, pos);
        temp += std::string("&");
   re    while ((pos = temp.find_first_of("&")) != std::string::npos) {
            std::string line = temp.substr(0, pos);
            temp = temp.substr(pos + 1);
            size_t ep = line.find_first_of("=");
            if (ep == std::string::npos) {
                continue;
            }
            para[line.substr(0, ep)] = tools::parse_url_para(line.substr(ep + 1));
        }
    }

    PRINTF("path = %s\n", path.c_str());
    std::map<std::string, std::string>::iterator    it;
    for (it = para.begin(); it != para.end(); it++) {
        PRINTF("[%s] => [%s]\n", it->first.c_str(), it->second.c_str());
    }
    if (para.find("Action") != para.end()) {
        para.clear();
    }

    if (!tools::IsFileExists(path.c_str())) {

        char tips[4096];
        sprintf(tips, "No such page: %s", path.c_str());
        char    tmp[10];
        sprintf(tmp, "%d", (int)strlen(tips));

        std::string cnt = MakeHttpHead(404, -1);
        cnt += "Content-Length: ";
        cnt += tmp;
        cnt += "\r\n";
        cnt += "\r\n";
        cnt += tips;
        cnt += std::string("\r\n\r\n");
        sockA.Send(cnt.c_str(), cnt.length());
        // PRINTF("Send:\n%s\n", cnt.c_str());
        return 404;
    }

    if (head.Method == "GET") {
    } else if (head.Method == "POST") {
    }

    std::string fl;
    fl = ParseFile(path.c_str(), para);

    std::string cnt = MakeHttpHead(200, fl.length());
    cnt += "\r\n";
    cnt += fl;
    cnt += "\r\n\r\n";
    sockA.Send(cnt.c_str(), cnt.length());


    return 0;
}

std::string Http::MakeHttpHead(int code, int ContextLength)
{
    char    scode[100];
    sprintf(scode, "%d", code);
    std::string head = "HTTP/1.1 ";
    head += scode;
    head += " ";
    head += codemap[code];
    head += "\r\n";
    head += "Server: kazuki's false\r\n";
    head += "Content-Type: text/html; charset=utf-8\r\n";
    if (ContextLength >= 0) {
        char temp[100];
        sprintf(temp, "Content-Length: %d\r\n", ContextLength);
        head += temp;
    }
    return head;
}

std::string Http::ParseFile(const char * file, std::map<std::string, std::string>&  para)
{
    FILE * fp = fopen(file, "rb");
    fseek(fp, 0, SEEK_END);
    int len = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    int total = 0;
    std::string ff;
    while (total < len) {
        char    buffer[4096];
        int ret = fread(buffer, 1, 4096, fp);
        if (ret <= 0)
            break;
        buffer[ret] = '\0';
        ff += std::string(buffer);
        total += ret;
    }
    fclose(fp);

    std::string t1, t2, t3;
    MakeRandomPair(para, t1, t2, t3);
    ff = tools::replace(ff, "<%PLAIN%>", t1);
    ff = tools::replace(ff, "<%EXPECT%>", t2);
    ff = tools::replace(ff, "<%TYPE%>", t3);

    if (para.empty())
        return ff;

    if (para["plain"].empty()) {
        return ff;
    }

    OutputResult(para);

    static std::list<CipherResult>  rlist;
    CipherResult    cr(para["plain"], para["cipher"], para["expect"], para["type"], para["pwd"]);
    rlist.push_front(cr);

    std::list<CipherResult>::iterator   it;
    std::stringstream   oss;

    int count = 0;
    for (it=rlist.begin(); it!=rlist.end(); it++) {
        count ++;
        oss << "<tr>";
        oss << "<td>";
        oss << "type   = " << (*it).type << "<br />";
        oss << "pass   = " << (*it).pass << "<br />";
        oss << "plain  = " << (*it).plain << "<br />";
        oss << "cipher = " << (*it).cipher << "<br />";
        oss << "expect = " << (*it).expect << "<br />";
        if (IsRandomPairEqual((*it).cipher, (*it).expect, (*it).pass, (*it).type)) {
            oss << "<font color=green>" << "OK" << "</font><br />";
            char    oc[4096];
            un3des((*it).expect.c_str(), (*it).pass.c_str(), (*it).type.c_str(), oc, sizeof(oc));
            oss << oc << "<br />";
        } else {
            oss << "<font color=red>" << "FAIL" << "</font><br />";
            char oe[4096];
            un3des((*it).cipher.c_str(), (*it).pass.c_str(), (*it).type.c_str(), oe, sizeof(oe));
            oss << oe << "<br />";
        }
        oss << "</td>";
        oss << "</tr>";
        if (count > 5)
            break;
    }
    std::string result = tools::replace(ff, "<!--%TABLE%-->", oss.str());
    return result;
}

void Http::MakeRandomPair(std::map<std::string, std::string>& para, std::string& plain, std::string& expect, std::string& type)
{
    if (para.empty()) {
        plain = "";
        expect = "";
        type = "";
    } else {
        int l = rand() % 100000;
        char    temp[100];
        sprintf(temp, "%X", l);

        plain = temp;
        l = (rand() % 3) + 1;
        sprintf(temp, "%04d", l);
        type = temp;
        char    output[4096] = {0};
        CTCGetAuthInfo(output, sizeof(output), plain.c_str(), 
            para["ip"].c_str(), para["mac"].c_str(), para["uid"].c_str(), para["sid"].c_str(),
            type.c_str(), para["pwd"].c_str());
        expect = output;
    }
}

bool Http::IsRandomPairEqual(std::string& cipher, std::string& expect, std::string& password, std::string& type)
{
    if (expect.empty() || cipher.empty())
        return false;
#if 0
    std::string e = expect.substr(16);
    std::string c = cipher.substr(cipher.length() - e.length());
    return e == c;
#else
    char oe[4096], oc[4096];
    un3des(cipher.c_str(), password.c_str(), type.c_str(), oe, sizeof(oe));
    un3des(expect.c_str(), password.c_str(), type.c_str(), oc, sizeof(oc));
    // printf("oe = [%s]\n", oe);
    // printf("oc = [%s]\n", oc);
    char * pe = strchr(oe, '$');
    char * pc = strchr(oc, '$');
    if (!pe || !pc)
        return false;
    if (strcmp(pe, pc) == 0)
        return true;
    return false;
#endif
}

void Http::OutputResult(std::map<std::string, std::string>& para)
{
    char oe[4096], oc[4096];
    un3des(para["cipher"].c_str(), para["pwd"].c_str(), para["type"].c_str(), oe, sizeof(oe));
    un3des(para["expect"].c_str(), para["pwd"].c_str(), para["type"].c_str(), oc, sizeof(oc));

    printf("type = %s\nplain= %s\ncipher = %s\ncipher = %s\nexpect = %s\nexpect = %s\n", 
        para["type"].c_str(), para["plain"].c_str(), para["cipher"].c_str(), oc, para["expect"].c_str(), oe);

    std::ofstream    oss("result.txt", std::ios::app);
    oss << para["type"]
        << '\t'
        << para["plain"]
        << '\t'
        << para["cipher"]
        << "\t"
        << para["expect"];
    if (IsRandomPairEqual(para["cipher"], para["expect"], para["pwd"], para["type"]))
        oss << '\t' << "PASS";
    else
        oss << '\t' << "FAIL";
    oss << '\n';
    oss.close();
}

int main()
{
    Http    http;
    tools::CSocket::InitNetwork();
    tools::CSocket  sockL(tools::CSocket::eSocketType_TCP);

    try {
        sockL.Create();
        sockL.Bind(10008);
        sockL.Listen();

        while (true) {
            sockL.Accept(sockA);
            while (true) {
                char    buffer[4096];
                int ret = sockA.Recv(buffer, sizeof(buffer));
                if (ret <= 0)
                    break;
                http.Read(buffer, ret);
            }
            sockA.Close();
        }
    } catch (const char * e) {
        PRINTF("error: %s\n", e);
    }

    return 0;
}















